using System;
using System.Diagnostics;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Publishing;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure")]
    [XmlRoot(Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure", IsNullable = false)]
    public class NavigationItem : BaseItem<NavigationItem>
    {
        public NavigationItem()
        {
            NavigationItems = new HierarchicalList<NavigationItem, NavigationItem>(this);
        }

        /// <remarks/>
        [XmlAttribute]
        public int Id { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Description { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool IsInvisible { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string ParentUrl { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string PreviousUrl { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool OpenInNewWindow { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool AddAsFirst { get; set; }

        /// <remarks/>
        [XmlElement("NavigationItem", typeof(NavigationItem))]
        public HierarchicalList<NavigationItem, NavigationItem> NavigationItems { get; set; }

        /// <summary>
        /// Gets a navigation item in a navigation object
        /// </summary>
        /// <param name="navigationItemDefinition">Xml object for a navigation item</param>
        /// <param name="navigation">A sharepoint navigation object</param>
        /// <returns>A sharepoint navigation node</returns>
        protected static internal SPNavigationNode GetNavigationItem(NavigationItem navigationItemDefinition, SPNavigation navigation)
        {
            Trace.WriteLine("Get navigation item '" + navigationItemDefinition.Title + "' with Url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:GetNavigationItem");

            string navigationItemUrl = GetRootedUrl(NamedElement.ResolveResource(navigationItemDefinition.Url,
                                                                                 navigation.Web.Locale),
                                                    navigation.Web.ServerRelativeUrl);
            string navigationItemTitle = NamedElement.ResolveResource(navigationItemDefinition.Title, navigation.Web.Locale);

            var navigationItem = (navigation.GetNodeById(navigationItemDefinition.Id)) ??
                                 GetNavigationItem(navigationItemUrl, navigationItemTitle, navigation.QuickLaunch.Parent);

            Trace.WriteLine("Got navigation item '" + navigationItemDefinition.Title + "' with Url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:GetNavigationItem");

            return navigationItem;
        }

        /// <summary>
        /// Gets a navigation item in a navigation object
        /// </summary>
        /// <param name="navigationItemUrl">The navigation item url</param>
        /// <param name="navigationItemTitle">The navigation item title</param>
        /// <param name="parentNavigationItem">The parent where to search for the navigation item</param>
        /// <returns>A sharepoint navigation node</returns>
        protected static internal SPNavigationNode GetNavigationItem(string navigationItemUrl, string navigationItemTitle, SPNavigationNode parentNavigationItem)
        {
            Trace.WriteLine("Try to get navigation item '" + navigationItemTitle + "' with Url '" + navigationItemUrl + "'.", "ContentStructureImporter:GetNavigationItem");

            for (int index = 0; index < parentNavigationItem.Children.Count; index++)
            {
                var subNavigationItem = parentNavigationItem.Children[index];
                if (subNavigationItem.Url == navigationItemUrl && subNavigationItem.Title == navigationItemTitle)
                    return subNavigationItem;
                if (subNavigationItem.Children.Count > 0)
                {
                    subNavigationItem = GetNavigationItem(navigationItemUrl, navigationItemTitle, subNavigationItem);

                    if (subNavigationItem != null)
                    {
                        Trace.WriteLine("Got navigation item '" + navigationItemTitle + "' with Url '" + navigationItemUrl + "'.", "ContentStructureImporter:GetNavigationItem");
                        return subNavigationItem;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Adds, Replaces, Deletes, Modifies or Moves a navigation item
        /// </summary>
        /// <param name="navigationItemDefinition">Xml object for a navigation item</param>
        /// <param name="navigationItem">A navigation item based on the navigationItemDefinition or null</param>
        /// <param name="parentNavigationItem">The parent where to import the navigation item</param>
        protected static internal void ImportNavigationItem(NavigationItem navigationItemDefinition, SPNavigationNode navigationItem, SPNavigationNode parentNavigationItem)
        {
            Trace.WriteLine("Try to import navigation item '" + navigationItemDefinition.Title + "' with Url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:ImportNavigationItem");

            SPWeb parentWeb = parentNavigationItem.Navigation.Web;

            if (navigationItem == null)
            {
                if (navigationItemDefinition.Action == ActionType.Add ||
                    navigationItemDefinition.Action == ActionType.Replace)
                    navigationItem = CreateNavigationItem(navigationItemDefinition, parentNavigationItem, parentWeb);
                else
                    return;
            }
            else if (navigationItemDefinition.Action == ActionType.Replace)
            {
                navigationItem.Delete();
                navigationItem = CreateNavigationItem(navigationItemDefinition, parentNavigationItem, parentWeb);
            }
            else if (navigationItemDefinition.Action == ActionType.Delete)
            {
                navigationItem.Delete();
                return;
            }

            for (int index = 0; index < navigationItemDefinition.NavigationItems.Count; index++)
            {
                NavigationItem subNavigationItemDefinition = navigationItemDefinition.NavigationItems[index];
                string subNavigationItemUrl = GetRootedUrl(NamedElement.ResolveResource(subNavigationItemDefinition.Url,
                                                                                        parentWeb.Locale),
                                                           parentWeb.ServerRelativeUrl);
                string subNavigationItemTitle = NamedElement.ResolveResource(subNavigationItemDefinition.Title,
                                                                                                        parentWeb.Locale);

                SPNavigationNode subNavigationItem = null;
                SPNavigationNode subNavigationParentItem = parentNavigationItem;

                // Find the explict defined parent
                if (!string.IsNullOrEmpty(subNavigationItemDefinition.ParentUrl))
                {
                    string subNavigationItemParentUrl =
                        GetRootedUrl(NamedElement.ResolveResource(subNavigationItemDefinition.ParentUrl,
                                                                  parentWeb.Locale),
                                     parentWeb.ServerRelativeUrl);

                    subNavigationParentItem = GetNavigationItem(subNavigationItemParentUrl, subNavigationItemTitle,
                                                                parentNavigationItem.Navigation.QuickLaunch.Parent) ??
                                              parentNavigationItem;
                }

                if (!string.IsNullOrEmpty(subNavigationItemUrl) && subNavigationItemUrl == "#")
                    subNavigationItem = GetNavigationItem(subNavigationItemUrl, subNavigationItemTitle, subNavigationParentItem);

                ImportNavigationItem(subNavigationItemDefinition, subNavigationItem, navigationItem);
            }

            if (navigationItemDefinition.Action != ActionType.None &&
                navigationItemDefinition.Action <= ActionType.Modify)
                ModifyNavigationItem(navigationItemDefinition, navigationItem, parentWeb);

            Trace.WriteLine("Navigation item '" + navigationItemDefinition.Title + "' with Url '" + navigationItemDefinition.Url + "' imported.", "ContentStructureImporter:ImportNavigationItem");
        }

        /// <summary>
        /// Modifies the properties of a navigation item 
        /// </summary>
        /// <param name="navigationItemDefinition">Xml object for a navigationItem</param>
        /// <param name="navigationItem">The parent navigation item where to create the navigation ite</param>
        /// <param name="parentWeb">The parent web where to create the navigation item</param>
        private static void ModifyNavigationItem(NavigationItem navigationItemDefinition, SPNavigationNode navigationItem, SPWeb parentWeb)
        {
            try
            {
                string navigationItemDescription = NamedElement.ResolveResource(navigationItemDefinition.Description,
                                                                                parentWeb.Locale);

                Trace.WriteLine("Try to modify navigationItem '" + navigationItemDefinition.Title + "' with url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:ModifyNavigationItem");
                ModifyProperty("Description", navigationItemDescription, false, navigationItem.Properties);
                ModifyProperty("Target", navigationItemDefinition.OpenInNewWindow.ToString(), false,
                               navigationItem.Properties);
                ModifyProperty("NodeType", navigationItemDefinition.NavigationItems.Count != 0 ? NodeTypes.Heading.ToString() : NodeTypes.AuthoredLinkPlain.ToString(), false, navigationItem.Properties);
                navigationItem.Title = navigationItemDefinition.Title;
                navigationItem.IsVisible = !navigationItemDefinition.IsInvisible;

                SPNavigationNode previousNavigationItem = null;
                SPNavigationNode parentNavigationItem = null;

                if (!string.IsNullOrEmpty(navigationItemDefinition.ParentUrl))
                {
                    string parentNavigationItemUrl =
                        GetRootedUrl(NamedElement.ResolveResource(navigationItemDefinition.ParentUrl,
                                                                  parentWeb.Locale),
                                     parentWeb.ServerRelativeUrl);
                    string parentNavigationItemTitle =
                        NamedElement.ResolveResource(navigationItemDefinition.Title,
                                                                  parentWeb.Locale);

                    parentNavigationItem = GetNavigationItem(parentNavigationItemUrl, parentNavigationItemTitle,
                                                             navigationItem.Navigation.QuickLaunch.Parent);
                }
                else if (navigationItemDefinition.Parent != null)
                    parentNavigationItem = GetNavigationItem(navigationItemDefinition.Parent, navigationItem.Navigation);
                else
                    parentNavigationItem = navigationItem.Parent;

                if (!string.IsNullOrEmpty(navigationItemDefinition.PreviousUrl))
                {
                    string navigationItemPreviousUrl =
                        GetRootedUrl(NamedElement.ResolveResource(navigationItemDefinition.PreviousUrl,
                                                                  parentWeb.Locale),
                                     parentWeb.ServerRelativeUrl);

                    string navigationItemPreviousTitle =
                        NamedElement.ResolveResource(navigationItemDefinition.Title,
                                                                  parentWeb.Locale);

                    previousNavigationItem = GetNavigationItem(navigationItemPreviousUrl, navigationItemPreviousTitle,
                                                               parentNavigationItem);
                }

                if (parentNavigationItem != null)
                {
                    if (previousNavigationItem != null)
                        navigationItem.Move(parentNavigationItem.Children, previousNavigationItem);
                    else if (navigationItemDefinition.AddAsFirst)
                        navigationItem.MoveToFirst(parentNavigationItem.Children);
                    else
                        navigationItem.MoveToLast(parentNavigationItem.Children);
                }
                else
                    navigationItem.Update();

                Trace.WriteLine("NavigationItem '" + navigationItemDefinition.Title + "' with url '" + navigationItemDefinition.Url + "' modified.", "ContentStructureImporter:ModifyNavigationItem");

            }
            catch (Exception)
            {
                Trace.WriteLine("Error on modifing navigationItem '" + navigationItemDefinition.Title + "' with url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:ModifyNavigationItem");
                throw new ContentStructureImporterException("Error on modifing node '" + navigationItemDefinition.Url ??
                                                            navigationItemDefinition.Id + "'");
            }
        }

        /// <summary>
        /// Creates a navigation item in a given web below a given navigation item
        /// </summary>
        /// <param name="navigationItemDefinition">Xml object for a navigationItem</param>
        /// <param name="parentNavigationItem">The parent navigation item where to create the navigation item</param>
        /// <param name="parentWeb">The parent web where to create the navigation item</param>
        /// <returns>The created sharepoint navigation node</returns>
        private static SPNavigationNode CreateNavigationItem(NavigationItem navigationItemDefinition, SPNavigationNode parentNavigationItem, SPWeb parentWeb)
        {
            Trace.WriteLine("Try to create navigation item '" + navigationItemDefinition.Title + "' with url '" + navigationItemDefinition.Url + "'.", "ContentStructureImporter:CreateNavigationItem");

            string navigationItemName = NamedElement.ResolveResource(navigationItemDefinition.Title, parentWeb.Locale);
            string navigationItemUrl = NamedElement.ResolveResource(navigationItemDefinition.Url, parentWeb.Locale);
            bool isExternal = navigationItemUrl.StartsWith("http") || navigationItemUrl.StartsWith(@"\\") || navigationItemUrl.StartsWith("/") || navigationItemUrl.StartsWith("#");

            SPNavigationNode previousNavigationItem = null;
            if (!string.IsNullOrEmpty(navigationItemDefinition.PreviousUrl))
            {
                string navigationItemPreviousUrl =
                    GetRootedUrl(NamedElement.ResolveResource(navigationItemDefinition.PreviousUrl,
                                                              parentWeb.Locale),
                                 parentWeb.ServerRelativeUrl);

                previousNavigationItem = GetNavigationItem(navigationItemPreviousUrl, navigationItemName,
                                                            parentNavigationItem);
            }

            var newNavigationItem = new SPNavigationNode(navigationItemName, navigationItemUrl, isExternal);

            //newNavigationNode.Id = navigationItemDefinition.Id;
            if (previousNavigationItem != null)
                newNavigationItem = parentNavigationItem.Children.Add(newNavigationItem, previousNavigationItem);
            else if (navigationItemDefinition.AddAsFirst)
                newNavigationItem = parentNavigationItem.Children.AddAsFirst(newNavigationItem);
            else
                newNavigationItem = parentNavigationItem.Children.AddAsLast(newNavigationItem);

            Trace.WriteLine("Navigation item '" + navigationItemDefinition.Title + "' with url '" + navigationItemDefinition.Url + "' created.", "ContentStructureImporter:CreateNavigationItem");

            return newNavigationItem;
        }
    }
}